package sifeap.dal.ejb;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.eclipse.persistence.internal.jpa.EJBQueryImpl;
import org.eclipse.persistence.jpa.JpaEntityManager;
import org.eclipse.persistence.queries.DatabaseQuery;
import org.eclipse.persistence.sessions.DatabaseRecord;
import org.eclipse.persistence.sessions.Session;

/**
 * Clase con los metodos basicos para la persistencia de una entidad JPA
 * 
 * 
 * @param <T>
 */
public abstract class AbstractFacade<T> {
	/**
	 * Entidad pasada como parametro
	 */
	private Class<T> entityClass;

	/**
	 * Constructor de la clase, con el parametro de entidad
	 * 
	 * @param entityClass
	 */
	public AbstractFacade(Class<T> entityClass) {
		this.entityClass = entityClass;
	}

	/**
	 * Retorna el EntityManager de la entidad
	 * 
	 * @return
	 */
	protected abstract EntityManager getEntityManager();

	/**
	 * Crea o persiste la entidad
	 * 
	 * @param entity
	 */
	public void crear(T entity) {
		getEntityManager().persist(entity);
	}

	/**
	 * edita la entidad
	 * 
	 * @param entity
	 */
	public void modificar(T entity) {
		getEntityManager().merge(entity);
	}

	/**
	 * elimina la entidad
	 * 
	 * @param entity
	 */
	public void eliminar(T entity) {
		getEntityManager().remove(getEntityManager().merge(entity));
	}

	/**
	 * Busca la entidad por si Id
	 * 
	 * @param id
	 * @return
	 */
	public T buscar(Object id) {
		return getEntityManager().find(entityClass, id);
	}

	/**
	 * Retorna una lista con todas las entidades T
	 * 
	 * @return
	 */
	public List<T> buscarTodos() {
		javax.persistence.criteria.CriteriaQuery cq = getEntityManager()
				.getCriteriaBuilder().createQuery();
		cq.select(cq.from(entityClass));
		return getEntityManager().createQuery(cq).getResultList();
	}

	/**
	 * Busca por rangos.
	 * 
	 * @param range
	 * @return
	 */
	public List<T> buscarPorRango(int[] range) {

		javax.persistence.criteria.CriteriaQuery cq = getEntityManager()
				.getCriteriaBuilder().createQuery();
		cq.select(cq.from(entityClass));
		javax.persistence.Query q = getEntityManager().createQuery(cq);
		q.setMaxResults(range[1] - range[0]);
		q.setFirstResult(range[0]);
		return q.getResultList();
	}

	/**
	 * Retrona el tamano de las entidades T
	 * 
	 * @return
	 */
	public int contar() {
		javax.persistence.criteria.CriteriaQuery cq = getEntityManager()
				.getCriteriaBuilder().createQuery();
		javax.persistence.criteria.Root<T> rt = cq.from(entityClass);
		cq.select(getEntityManager().getCriteriaBuilder().count(rt));
		javax.persistence.Query q = getEntityManager().createQuery(cq);
		return ((Long) q.getSingleResult()).intValue();
	}

	public void remover(T entity) {
		getEntityManager().remove(entity);
	}

	//PSP_METRICS_METHOD_BEGIN: consultarQuery
	/**
	 * Consulta un query parametrizado en los XML	 * 
	 * @param <T> tipo de entidad que va a mapear la consulta
	 * @param nombreConculta -- Nombre de la consulta a buscar en los query-*.xml
	 * @param parametros -- Var-args con los parametros de la consulta
	 * @return <T> List<T>
	 */
	protected <T> List<T> consultarQuery (Class<T> tipoRetorno, final String nombreConculta, Object... parametros) {
		List<T> resultado = null;
		final Query query = getEntityManager().createNamedQuery(nombreConculta);
		int indiceParametro = 1;
		if(parametros !=null && parametros.length > 0){
			for (Object param : parametros) {
				query.setParameter(indiceParametro++, param);				
			}
		}		
		query.setHint("javax.persistence.cache.storeMode", "REFRESH");
		resultado = query.getResultList();

		return resultado;
	}
	// PSP_METRICS_METHOD_END
	
	// PSP_METRICS_METHOD_BEGIN: siguienteValor
	/**
	 * Retorna el siguiente valor de la secuencia <code>nombreSecuencia</code>
	 * @param nombreSecuencia
	 * @return Siguiente valor de la secuencia
	 */
	public Long siguienteValor(String nombreSecuencia) {
		Long id = null;
		try {
			EntityManager em = getEntityManager();
			String sqlString = obtenerConsulta("siguienteValorSecuencia");
			sqlString = sqlString.replace("nombre_secuencia", nombreSecuencia);
			Query query = em.createNativeQuery(sqlString);
			id = ((Long) query.getSingleResult());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return id;
	}
	// PSP_METRICS_METHOD_END
	
	public String obtenerConsulta(String nombreConsulta) {
		EntityManager em = getEntityManager();
		Query query = em.createNamedQuery(nombreConsulta);

		Session session = em.unwrap(JpaEntityManager.class).getActiveSession();
		DatabaseQuery databaseQuery = ((EJBQueryImpl<?>) query).getDatabaseQuery();
		databaseQuery.prepareCall(session, new DatabaseRecord());
		String sqlString = databaseQuery.getSQLString();
		
		return sqlString;
	}
}
